home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 October / EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso / Aminet / gfx / show / svoUtah22.lha / svoUtahRLE / source / URT / lib / scanargs.c < prev    next >
C/C++ Source or Header  |  1993-12-22  |  21KB  |  885 lines

  1. /* 
  2.  * $Id: scanargs.c,v 3.0.1.1 90/11/19 17:04:44 spencer Exp $
  3.  *         Version 7 compatible
  4.  *     Argument scanner, scans argv style argument list.
  5.  * 
  6.  *     Some stuff is a kludge because sscanf screws up
  7.  * 
  8.  *     Gary Newman - 10/4/1979 - Ampex Corp. 
  9.  * 
  10.  *     Modified by Spencer W. Thomas, Univ. of Utah, 5/81 to
  11.  *     add args introduced by     a flag, add qscanargs call,
  12.  *     allow empty flags.
  13.  * 
  14.  *     If you make improvements we'd like to get them too.
  15.  *     Jay Lepreau    lepreau@utah-20, decvax!harpo!utah-cs!lepreau
  16.  *     Spencer Thomas    thomas@utah-20, decvax!harpo!utah-cs!thomas 
  17.  * 
  18.  *    (I know the code is ugly, but it just grew, you see ...)
  19.  * 
  20.  * Modified by:    Spencer W. Thomas
  21.  *     Date:    Feb 25 1983
  22.  * 1. Fixed scanning of optional args.  Now args introduced by a flag
  23.  *    must follow the flag which introduces them and precede any other
  24.  *    flag argument.  It is still possible for a flag introduced
  25.  *    argument to be mistaken for a "bare" argument which occurs
  26.  *    earlier in the format string.  This implies that flags may not
  27.  *    be conditional upon other flags, and a message will be generated
  28.  *    if this is attempted.
  29.  * 
  30.  * 2. Usage message can be formatted by inserting newlines, tabs and
  31.  *    spaces into the format string.  This is especially useful for
  32.  *    long argument lists.
  33.  * 
  34.  * 3. Added n/N types for "numeric" args.  These args are scanned
  35.  *    using the C language conventions - a number starting 0x is
  36.  *    hexadecimal, a number starting with 0 is octal, otherwise it is
  37.  *    decimal.
  38.  *
  39.  *  Modified at BRL 16-May-88 by Mike Muuss to avoid Alliant STDC desire
  40.  *  to have all "void" functions so declared.
  41.  */
  42.  
  43. #include "rle_config.h"
  44. #include <stdio.h>
  45. #include <ctype.h>
  46. #ifndef USE_STDARG
  47. #include <varargs.h>
  48. #else
  49. #include <stdarg.h>
  50. #endif
  51.  
  52. #ifdef USE_STDLIB_H
  53. #include <stdlib.h>
  54. #else
  55.  
  56. #ifndef VOID_STAR
  57. extern char *malloc();
  58. extern char *realloc();
  59. #else
  60. extern void *malloc();
  61. extern void *realloc();
  62. #endif
  63. void free();
  64.  
  65. #endif /* USE_STDLIB_H */
  66. typedef char bool;
  67. /* 
  68.  * An explicit assumption is made in this code that all pointers look
  69.  * alike, except possible char * pointers.
  70.  */
  71. typedef int *ptr;
  72.  
  73. #ifndef CONST_DECL
  74. #define CONST_DECL
  75. #endif
  76.  
  77. #define YES 1
  78. #define NO 0
  79. #define ERROR(msg)  {fprintf(stderr, "%s\n", msg); goto error; }
  80.  
  81. /* 
  82.  * Storage allocation macros
  83.  */
  84. #define NEW( type, cnt )    (type *) malloc( (cnt) * sizeof( type ) )
  85. #define RENEW( type, ptr, cnt )    (type *) realloc( ptr, (cnt) * sizeof( type ) )
  86.  
  87. #if defined(c_plusplus) && !defined(USE_PROTOTYPES)
  88. #define USE_PROTOTYPES
  89. #endif
  90.  
  91. #ifndef USE_PROTOTYPES
  92. static char * prformat();
  93. static int isnum();
  94. static int    _do_scanargs();
  95. void        scan_usage();
  96. #else
  97. static CONST_DECL char * prformat( CONST_DECL char *, int );
  98. static int isnum( CONST_DECL char *, int, int );
  99. static int    _do_scanargs( int argc, char **argv, CONST_DECL char *format,
  100.                   va_list argl );
  101. void        scan_usage( char **, CONST_DECL char * );
  102. #endif
  103.  
  104. /* 
  105.  * Argument list is (argc, argv, format, ... )
  106.  */
  107. int
  108. #ifndef USE_STDARG
  109. scanargs ( va_alist )
  110. va_dcl
  111. #else
  112. scanargs ( int argc, char **argv, CONST_DECL char *format, ... )
  113. #endif /* !USE_STDARG */
  114. {
  115.     va_list argl;
  116.     int retval;
  117. #ifndef USE_STDARG
  118.     int argc;
  119.     char ** argv;
  120.     CONST_DECL char *format;
  121.  
  122.     va_start( argl );
  123.     argc = va_arg( argl, int );
  124.     argv = va_arg( argl, char ** );
  125.     format = va_arg( argl, CONST_DECL char * );
  126. #else
  127.     va_start( argl, format );
  128. #endif
  129.     retval = _do_scanargs( argc, argv, format, argl );
  130.     va_end( argl );
  131.     return retval;
  132. }
  133.     
  134. /* 
  135.  * This routine is necessary because of a pyramid compiler botch that
  136.  * uses parameter registers in a varargs routine.  The extra
  137.  * subroutine call isolates the args on the register stack so they
  138.  * don't get trashed.
  139.  */
  140.  
  141. static int
  142. _do_scanargs( argc, argv, format, argl )
  143. int     argc;            /* Actual arguments */
  144. char  **argv;
  145. CONST_DECL char   *format;
  146. va_list argl;
  147. {
  148.  
  149.     register    check;            /* check counter to be sure all argvs
  150.                        are processed */
  151.     register CONST_DECL char  *cp;
  152.     register    cnt;
  153.     int        optarg = 0;            /* where optional args start */
  154.     int        nopt = 0;
  155.     char    tmpflg,            /* temp flag */
  156.         typchr;            /* type char from format string */
  157.     char    c;
  158.     bool  * arg_used;            /* array of flags */
  159.     ptr        aptr = 0;            /* pointer to return loc */
  160.  
  161.     bool    required;
  162.     int        excnt;            /* which flag is set */
  163.     bool    exflag;            /* when set, one of a set of exclusive
  164.                        flags is set */
  165.  
  166.     bool    list_of;            /* set if parsing off a list of args */
  167.     bool    comma_list;            /* set if AT&T style multiple args */
  168.     int      * cnt_arg = 0;        /* where to stuff list count */
  169.     int        list_cnt;            /* how many in list */
  170.     /* These are used to build return lists */
  171.     char ** strlist = 0;
  172.     int   * intlist = 0;
  173.     long  * longlist = 0;
  174.     float * fltlist = 0;
  175.     double *dbllist = 0;
  176.     char  * argp;            /* Pointer to argument. */
  177.  
  178.     CONST_DECL char   *ncp;        /* remember cp during flag scanning */
  179.     static char   cntrl[7] = "%  %1s";    /* control string for scanf's */
  180.     char    junk[2];            /* junk buffer for scanf's */
  181.  
  182.     arg_used = NEW( bool, argc );
  183.     if (arg_used == NULL)
  184.     {
  185.     fprintf(stderr, "malloc failed in scanargs, exiting\n");
  186.     exit(-1);
  187.     }
  188.     else
  189.     {
  190.     for (cnt=0; cnt<argc; cnt++)
  191.         arg_used[cnt] = NO;
  192.     }
  193.  
  194.     check = 0;
  195.     cp = format;
  196.     /* 
  197.      * Skip program name
  198.      */
  199.     while ( *cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0' )
  200.     cp++;
  201.  
  202.     while (*cp)
  203.     {
  204.     required = NO;            /* reset per-arg flags */
  205.     list_of = NO;
  206.     comma_list = NO;
  207.     list_cnt = 0;
  208.     switch (*(cp++))
  209.     {
  210.         default:             /* all other chars */
  211.         break;
  212.         case ' ':            /* separators */
  213.         case '\t':
  214.         case '\n':
  215.         optarg = 0;        /* end of optional arg string */
  216.         break;
  217.  
  218.         case '!':             /* required argument */
  219.         required = YES;
  220.         case '%':             /* not required argument */
  221. reswitch:                /* after finding '*' or ',' */
  222.         switch (typchr = *(cp++))
  223.         {
  224.             case ',':        /* argument is AT&T list of things */
  225.             comma_list = YES;
  226.             case '*':        /* argument is list of things */
  227.             list_of = YES;
  228.             list_cnt = 0;    /* none yet */
  229.             cnt_arg = va_arg( argl, int *);    /* item count * here */
  230.             goto reswitch;    /* try again */
  231.  
  232.             case '$':        /* "rest" of argument list */
  233.             while ( argc > 1 && !arg_used[argc-1] )
  234.                 argc--;    /* find last used argument */
  235.             *va_arg( argl, int * ) = argc;
  236.             break;
  237.  
  238.             case '-':         /* argument is flag */
  239.             if (optarg > 0)
  240.                 ERROR("Format error: flag conditional on flag not allowed");
  241.  
  242.             /* go back to label */
  243.             ncp = cp-1;    /* remember */
  244.             cp -= 3;
  245.             for (excnt = exflag = 0
  246.                 ; *cp != ' ' && !(*cp=='-' &&(cp[-1]=='!'||cp[-1]=='%'));
  247.                 (--cp, excnt++))
  248.             {
  249.                 for (cnt = optarg+1; cnt < argc; cnt++)
  250.                 {
  251.                 /* flags all start with - */
  252.                 if (*argv[cnt] == '-' && !arg_used[cnt] &&
  253.                     !isdigit(argv[cnt][1]))
  254.                     if (*(argv[cnt] + 1) == *cp)
  255.                     {
  256.                     if (*(argv[cnt] + 2) != 0)
  257.                         ERROR ("extra flags ignored");
  258.                     if (exflag)
  259.                         ERROR ("more than one exclusive flag chosen");
  260.                     exflag++;
  261.                     required = NO;
  262.                     check += cnt;
  263.                     arg_used[cnt] = 1;
  264.                     nopt = cnt;
  265.                     *va_arg( argl, int *) |= (1 << excnt);
  266.                     break;
  267.                     }
  268.                 }
  269.             }
  270.             if (required)
  271.                 ERROR ("flag argument missing");
  272.             cp = ncp;
  273.             /* 
  274.              * If none of these flags were found, skip any
  275.              * optional arguments (in the varargs list, too).
  276.              */
  277.             if (!exflag)
  278.             {
  279.                 (void)va_arg( argl, int * );/* skip the arg, too */
  280.                 while (*++cp && ! isspace(*cp))
  281.                 if (*cp == '!' || *cp == '%')
  282.                 {
  283.                     if ( *++cp == '*' || *cp == ',' )
  284.                     {
  285.                     cp++;
  286.                     (void)va_arg( argl, int * );
  287.                     }
  288.                     /* 
  289.                      * Assume that char * might be a
  290.                      * different size, but that all
  291.                      * other pointers are same size.
  292.                      */
  293.                     if ( *cp == 's' )
  294.                     (void)va_arg( argl, char * );
  295.                     else
  296.                     (void)va_arg( argl, ptr );
  297.                 }
  298.             }
  299.             else
  300.             {
  301.                 optarg = nopt;
  302.                 cp++;    /* skip over - */
  303.             }
  304.  
  305.             break;
  306.  
  307.             case 's':         /* char string */
  308.             case 'd':         /* decimal # */
  309.             case 'o':         /* octal # */
  310.             case 'x':         /* hexadecimal # */
  311.             case 'n':        /* "number" in C syntax */
  312.             case 'f':         /* floating # */
  313.             case 'D':         /* long decimal # */
  314.             case 'O':         /* long octal # */
  315.             case 'X':         /* long hexadecimal # */
  316.             case 'N':        /* long number in C syntax */
  317.             case 'F':         /* double precision floating # */
  318. #if defined(sgi) && !defined(mips)
  319.             /* Fix for broken SGI IRIS 2400/3000 floats */
  320.             if ( typchr == 'F' ) typchr = 'f';
  321. #endif /* sgi */
  322.             for (cnt = optarg+1; cnt < argc; cnt++)
  323.             {
  324.                 argp = argv[cnt];
  325.  
  326.                 if ( isnum( argp, typchr, comma_list ) )
  327.                 {
  328.                 ;    /* it's ok, then */
  329.                 }
  330.                 else if ( *argp == '-' && argp[1] != '\0' )
  331.                 if ( optarg > 0 ) /* end optional args? */
  332.                 {
  333.                     /* Eat the arg, too, if necessary */
  334.                     if ( list_cnt == 0 )
  335.                     if ( typchr == 's' )
  336.                         (void)va_arg( argl, char * );
  337.                     else
  338.                         (void)va_arg( argl, ptr );
  339.                     break;
  340.                 }
  341.                 else
  342.                     continue;
  343.                 else if ( typchr != 's' )
  344.                 continue;    /* not number, keep looking */
  345.                 
  346.                 /* 
  347.                  * Otherwise usable argument may already
  348.                  * be used.  (Must check this after
  349.                  * checking for flag, though.)
  350.                  */
  351.                 if (arg_used[cnt]) continue;
  352.  
  353.                 /* 
  354.                  * If it's a comma-and-or-space-separated
  355.                  * list then count how many, and separate
  356.                  * the list into an array of strings.
  357.                  */
  358.                 if ( comma_list )
  359.                 {
  360.                 register char * s;
  361.                 int pass;
  362.  
  363.                 /* 
  364.                  * On pass 0, just count them.  On
  365.                  * pass 1, null terminate each string 
  366.                  */
  367.                 for ( pass = 0; pass <= 1; pass++ )
  368.                 {
  369.                     for ( s = argp; *s != '\0'; )
  370.                     {
  371.                     if ( pass )
  372.                         strlist[list_cnt] = s;
  373.                     while ( (c = *s) != '\0' && c != ' ' &&
  374.                         c != '\t' && c != ',' )
  375.                         s++;
  376.                     if ( pass )
  377.                         *s = '\0';
  378.  
  379.                     list_cnt++;    /* count separators */
  380.                     /* 
  381.                      * Two commas in a row give a null
  382.                      * string, but two spaces
  383.                      * don't.  Also skip spaces
  384.                      * after a comma.
  385.                      */
  386.                     if ( c != '\0' )
  387.                         while ( *++s == ' ' || *s == '\t' )
  388.                         ;
  389.                     }
  390.                     if ( pass == 0 )
  391.                     {
  392.                     strlist = NEW( char *, list_cnt );
  393.                     list_cnt = 0;
  394.                     }
  395.                 }
  396.                 }
  397.                 else if ( list_of )
  398.                 list_cnt++;   /* getting them one at a time */
  399.                 /* 
  400.                  * If it's either type of list, then alloc
  401.                  * storage space for the returned values
  402.                  * (except that comma-separated string
  403.                  * lists already are done).
  404.                  */
  405.                 if ( list_of )
  406.                 {
  407.                 if ( list_cnt == 1 || comma_list )
  408.                     switch( typchr )
  409.                     {
  410.                     case 's':
  411.                         if ( !comma_list )
  412.                         strlist = NEW( char *, 1 );
  413.                         aptr = (ptr) &strlist[0];
  414.                         break;
  415.                     case 'n':
  416.                     case 'd':
  417.                     case 'o':
  418.                     case 'x':
  419.                         intlist = NEW( int, list_cnt );
  420.                         aptr = (ptr) &intlist[0];
  421.                         break;
  422.                     case 'N':
  423.                     case 'D':
  424.                     case 'O':
  425.                     case 'X':
  426.                         longlist = NEW( long, list_cnt );
  427.                         aptr = (ptr) &longlist[0];
  428.                         break;
  429.                     case 'f':
  430.                         fltlist = NEW( float, list_cnt );
  431.                         aptr = (ptr) &fltlist[0];
  432.                         break;
  433.                     case 'F':
  434.                         dbllist = NEW( double, list_cnt );
  435.                         aptr = (ptr) &dbllist[0];
  436.                         break;
  437.                     }
  438.                 else
  439.                     switch( typchr )
  440.                     {
  441.                     case 's':
  442.                         strlist = RENEW( char *, strlist,
  443.                                  list_cnt );
  444.                         aptr = (ptr) &strlist[list_cnt-1];
  445.                         break;
  446.                     case 'n':
  447.                     case 'd':
  448.                     case 'o':
  449.                     case 'x':
  450.                         intlist = RENEW( int, intlist,
  451.                                  list_cnt );
  452.                         aptr = (ptr) &intlist[list_cnt-1];
  453.                         break;
  454.                     case 'N':
  455.                     case 'D':
  456.                     case 'O':
  457.                     case 'X':
  458.                         longlist = RENEW( long, longlist,
  459.                                   list_cnt );
  460.                         aptr = (ptr) &longlist[list_cnt-1];
  461.                         break;
  462.                     case 'f':
  463.                         fltlist = RENEW( float, fltlist,
  464.                                  list_cnt );
  465.                         aptr = (ptr) &fltlist[list_cnt-1];
  466.                         break;
  467.                     case 'F':
  468.                         dbllist = RENEW( double, dbllist,
  469.                                  list_cnt );
  470.                         aptr = (ptr) &dbllist[list_cnt-1];
  471.                         break;
  472.                     }
  473.                 }
  474.                 else
  475.                 aptr = va_arg( argl, ptr );
  476.  
  477.                 if ( typchr == 's' )
  478.                 {
  479.                 if ( ! comma_list )
  480.                     *(char **)aptr = argp;
  481.                 }
  482.                 else
  483.                 {
  484.                 nopt = 0;
  485.                 do {
  486.                     /* 
  487.                      * Need to update aptr if parsing
  488.                      * a comma list
  489.                      */
  490.                     if ( comma_list && nopt > 0 )
  491.                     {
  492.                     argp = strlist[nopt];
  493.                     switch( typchr )
  494.                     {
  495.                         case 'n':
  496.                         case 'd':
  497.                         case 'o':
  498.                         case 'x':
  499.                         aptr = (ptr) &intlist[nopt];
  500.                         break;
  501.                         case 'N':
  502.                         case 'D':
  503.                         case 'O':
  504.                         case 'X':
  505.                         aptr = (ptr) &longlist[nopt];
  506.                         break;
  507.                         case 'f':
  508.                         aptr = (ptr) &fltlist[nopt];
  509.                         break;
  510.                         case 'F':
  511.                         aptr = (ptr) &dbllist[nopt];
  512.                         break;
  513.                     }
  514.                     }
  515.                     /* 
  516.                      * Do conversion for n and N types
  517.                      */
  518.                     tmpflg = typchr;
  519.                     if (typchr == 'n' || typchr == 'N' )
  520.                     if (*argp != '0')
  521.                         tmpflg = 'd';
  522.                     else if (*(argp+1) == 'x' ||
  523.                          *(argp+1) == 'X')
  524.                     {
  525.                         tmpflg = 'x';
  526.                         argp += 2;
  527.                     }
  528.                     else
  529.                         tmpflg = 'o';
  530.                     if (typchr == 'N')
  531.                     tmpflg = toupper( tmpflg );
  532.  
  533.  
  534.                     /* put in conversion */
  535.                     if ( isupper( tmpflg ) )
  536.                     {
  537.                     cntrl[1] = 'l';
  538.                     cntrl[2] = tolower( tmpflg );
  539.                     }
  540.                     else
  541.                     {
  542.                     cntrl[1] = tmpflg;
  543.                     cntrl[2] = ' ';
  544.                     }
  545.                     if (sscanf (argp, cntrl, aptr, junk) != 1)
  546.                     ERROR ("Bad numeric argument");
  547.                 } while ( comma_list && ++nopt < list_cnt );
  548.                 }
  549.                 check += cnt;
  550.                 arg_used[cnt] = 1;
  551.                 required = NO;
  552.                 /*
  553.                  * If not looking for multiple args,
  554.                  * then done, otherwise, keep looking.
  555.                  */
  556.                 if ( !( list_of && !comma_list ) )
  557.                 break;
  558.                 else
  559.                 continue;
  560.             }
  561.             if (required)
  562.                 switch (typchr)
  563.                 {
  564.                 case 'x': 
  565.                 case 'X': 
  566.                     ERROR ("missing hexadecimal argument");
  567.                 case 's': 
  568.                     ERROR ("missing string argument");
  569.                 case 'o': 
  570.                 case 'O': 
  571.                     ERROR ("missing octal argument");
  572.                 case 'd': 
  573.                 case 'D': 
  574.                     ERROR ("missing decimal argument");
  575.                 case 'f': 
  576.                 case 'F': 
  577.                     ERROR ("missing floating argument");
  578.                 case 'n':
  579.                 case 'N':
  580.                     ERROR ("missing numeric argument");
  581.                 }
  582.             if ( list_cnt > 0 )
  583.             {
  584.                 *cnt_arg = list_cnt;
  585.                 switch ( typchr )
  586.                 {
  587.                 case 's':
  588.                     *va_arg( argl, char *** ) = strlist;
  589.                     break;
  590.                 case 'n':
  591.                 case 'd':
  592.                 case 'o':
  593.                 case 'x':
  594.                     *va_arg( argl, int ** ) = intlist;
  595.                     break;
  596.                 case 'N':
  597.                 case 'D':
  598.                 case 'O':
  599.                 case 'X':
  600.                     *va_arg( argl, long ** ) = longlist;
  601.                     break;
  602.                 case 'f':
  603.                     *va_arg( argl, float ** ) = fltlist;
  604.                     break;
  605.                 case 'F':
  606.                     *va_arg( argl, double **) = dbllist;
  607.                     break;
  608.                 }
  609.                 if ( typchr != 's' && comma_list )
  610.                 free( (char *) strlist );
  611.             }
  612.             else if ( cnt >= argc )
  613.             {
  614.                 /* Fell off end looking, so must eat the arg */
  615.                 if ( typchr == 's' )
  616.                 (void)va_arg( argl, char * );
  617.                 else
  618.                 (void)va_arg( argl, ptr );
  619.             }
  620.             break;
  621.             default:         /* error */
  622.             fprintf (stderr,
  623.                  "scanargs: Corrupt or invalid format spec\n");
  624.             return (0);
  625.         }
  626.     }
  627.     }
  628.  
  629.     /*  Count up empty flags */
  630.     for (cnt=1; cnt<argc; cnt++)
  631.     if (argv[cnt][0] == '-' && argv[cnt][1] == '-' && argv[cnt][2] == 0
  632.         && !arg_used[cnt] )
  633.         check += cnt;
  634.  
  635.     /* sum from 1 to N = n*(n+1)/2 used to count up checks */
  636.     if (check != (((argc - 1) * argc) / 2))
  637.     ERROR ("extra arguments not processed");
  638.  
  639.     free(arg_used);
  640.     return (1);
  641.  
  642. error: 
  643.     scan_usage( argv, format );
  644.     free(arg_used);
  645.     return 0;
  646. }
  647.  
  648. void
  649. scan_usage( argv, format )
  650. char ** argv;
  651. CONST_DECL char * format;
  652. {
  653.     register CONST_DECL char * cp;
  654.  
  655.     fprintf (stderr, "usage : ");
  656.     if (*(cp = format) != ' ')
  657.     {
  658. #ifdef AMIGA
  659.     char tmp;
  660. #endif
  661.     if ( *cp == '%' )
  662.     {
  663.         /* 
  664.          * This is bogus, but until everyone can agree on a name
  665.          * for (rindex/strrchr) ....
  666.          */
  667.         for ( cp = argv[0]; *cp != '\0'; cp++ )
  668.         ;            /* find the end of the string */
  669.         for ( ; cp > argv[0] && *cp != '/'; cp-- )
  670.         ;            /* find the last / */
  671.         if ( *cp == '/' )
  672.         cp++;
  673.         fprintf( stderr, "%s", cp );
  674.  
  675.         cp = format + 1;        /* reset to where it should be */
  676. #ifdef AMIGA
  677.         tmp = *cp++;
  678. #endif
  679.     }
  680. #ifdef AMIGA
  681.     while (putc (tmp, stderr) != ' ');
  682. #else
  683.     while (putc (*cp++, stderr) != ' ');
  684. #endif
  685.     }
  686.     else
  687.     fprintf (stderr, "?? ");
  688.     while (*cp == ' ')
  689.     cp++;
  690.     (void)prformat (cp, NO);
  691. }
  692.  
  693. static CONST_DECL char *
  694. prformat (format, recurse)
  695. CONST_DECL char   *format;
  696. int     recurse;
  697. {
  698.     register CONST_DECL char  *cp;
  699.     bool    required, comma_list;
  700.     int    list_of;
  701.  
  702.     cp = format;
  703.     if (recurse)
  704.     putc (' ', stderr);
  705.  
  706.     required = NO;
  707.     list_of = 0;
  708.     comma_list = NO;
  709.     while (*cp)
  710.     {
  711.     switch (*cp)
  712.     {
  713.         default:
  714.             cp++;
  715.         break;
  716.         case ' ':
  717.         case '\n':
  718.         case '\t':
  719.         /* allow annotations */
  720.         for ( ; format < cp; format++ )
  721.             putc( *format, stderr );
  722.         putc(*cp, stderr);
  723.         format = ++cp;
  724.         break;
  725.         case '!': 
  726.         required = YES;
  727.         case '%': 
  728. reswitch:
  729.         switch (*++cp)
  730.         {
  731.             case ',':
  732.             comma_list++;
  733.             case '*':
  734.             list_of++;
  735.             goto reswitch;
  736.  
  737.             case '$':        /* "rest" of argument list */
  738.             if (!required)
  739.                 putc ('[', stderr);
  740.             for (; format < cp - 1 - list_of; format++)
  741.                 putc (*format, stderr);
  742.             fputs( " ...", stderr );
  743.             if ( !required )
  744.                 putc( ']', stderr );
  745.             break;
  746.  
  747.             case '-':         /* flags */
  748.             if (!required)
  749.                 putc ('[', stderr);
  750.             putc ('-', stderr);
  751.  
  752.             if (cp - format > 2 + list_of)
  753.                 putc ('{', stderr);
  754.             cp = format;
  755.             while (*cp != '%' && *cp != '!')
  756. #ifdef AMIGA
  757.             {
  758.                 char tmp = *cp++;
  759.                 putc (tmp, stderr);
  760.             }
  761. #else
  762.                 putc (*cp++, stderr);
  763. #endif
  764.             if (cp - format > 1 + list_of)
  765.                 putc ('}', stderr);
  766.             cp += 2;    /* skip !- or %- */
  767.             if (*cp && !isspace(*cp))
  768.                 cp = prformat (cp, YES);
  769.                     /* this is a recursive call */
  770.  
  771.             cp--;    /* don't ignore next character */
  772.  
  773.             if (!required)
  774.                 putc (']', stderr);
  775.             break;
  776.             case 's':         /* char string */
  777.             case 'd':         /* decimal # */
  778.             case 'o':         /* octal # */
  779.             case 'x':         /* hexadecimal # */
  780.             case 'f':         /* floating # */
  781.             case 'D':         /* long decimal # */
  782.             case 'O':         /* long octal # */
  783.             case 'X':         /* long hexadecimal # */
  784.             case 'F':         /* double precision floating # */
  785.             case 'n':        /* numeric arg (C format) */
  786.             case 'N':        /* long numeric arg */
  787.             if (!required)
  788.                 putc ('[', stderr);
  789.             for (; format < cp - 1 - list_of; format++)
  790.                 putc (*format, stderr);
  791.             if ( list_of != 0 )
  792.             {
  793.                 if ( comma_list )
  794.                 putc( ',', stderr );
  795.                 else
  796.                 putc( ' ', stderr );
  797.                 fputs( "...", stderr );
  798.             }
  799.             if (!required)
  800.                 putc (']', stderr);
  801.             break;
  802.             default: 
  803.             break;
  804.         }
  805.         required = NO;
  806.         list_of = NO;
  807.         comma_list = NO;
  808.         if (*cp)        /* check for end of string */
  809.             format = ++cp;
  810.         if (*cp && !isspace(*cp))
  811.             putc (' ', stderr);
  812.     }
  813.     if (recurse && isspace(*cp))
  814.         break;
  815.     }
  816.     if (!recurse)
  817.     {
  818.     for ( ; format < cp; format++ )
  819.         putc( *format, stderr );
  820.     putc ('\n', stderr);
  821.     }
  822.     return (cp);
  823. }
  824.  
  825. /* 
  826.  * isnum - determine whether a string MIGHT represent a number.
  827.  * typchr indicates the type of argument we are looking for, and
  828.  * determines the legal character set.  If comma_list is YES, then
  829.  * space and comma are also legal characters.
  830.  */
  831. static int
  832. isnum( str, typchr, comma_list )
  833. register CONST_DECL char * str;
  834. int typchr;
  835. int comma_list;
  836. {
  837.     register CONST_DECL char *allowed, *digits, *cp;
  838.     int hasdigit = NO;
  839.  
  840.     switch( typchr )
  841.     {
  842.     case 'n':
  843.     case 'N':
  844.         allowed = " \t,+-x0123456789abcdefABCDEF";
  845.         break;
  846.     case 'd':
  847.     case 'D':
  848.         allowed = " \t,+-0123456789";
  849.         break;
  850.     case 'o':
  851.     case 'O':
  852.         allowed = " \t,01234567";
  853.         break;
  854.     case 'x':
  855.     case 'X':
  856.         allowed = " \t,0123456789abcdefABCDEF";
  857.         break;
  858.     case 'f':
  859.     case 'F':
  860.         allowed = " \t,+-eE.0123456789";
  861.         break;
  862.     case 's':            /* only throw out decimal numbers */
  863.     default:
  864.         allowed = " \t,+-.0123456789";
  865.         break;
  866.     }
  867.     digits = allowed;
  868.     while ( *digits != '0' )
  869.     digits++;
  870.     if ( ! comma_list )
  871.     allowed += 3;              /* then don't allow space, tab, comma */
  872.  
  873.     while ( *str != '\0' )
  874.     {
  875.         for ( cp = allowed; *cp != '\0' && *cp != *str; cp++ )
  876.             ;
  877.         if ( *cp == '\0' )
  878.         return NO;             /* if not in allowed chars, not number */
  879.     if ( cp - digits >= 0 )
  880.         hasdigit = YES;
  881.     str++;
  882.     }
  883.     return hasdigit;
  884. }
  885.